using System;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;
using W3C.Soap;

using ds = Microsoft.Dss.Services.Directory;
using dssp = Microsoft.Dss.ServiceModel.Dssp;
using submgr = Microsoft.Dss.Services.SubscriptionManager;
using bumper = Microsoft.Robotics.Services.ContactSensor.Proxy;

namespace UrbanBumper
{
    [DisplayName("Urban Bumper")]
    [Description("Urban Bumper")]
    [Contract(Contract.Identifier)]
    class UrbanBumperService : DsspServiceBase
    {
        [ServiceState]
        bumper.ContactSensor _state = new bumper.ContactSensor();

        [ServicePort("/UrbanBumper", AllowMultipleInstances = true)]
        UrbanBumperOperations _mainPort = new UrbanBumperOperations();

        [Partner("SubMgr", Contract = submgr.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.CreateAlways)]
        private submgr.SubscriptionManagerPort _subMgrPort = new submgr.SubscriptionManagerPort();

        public UrbanBumperService(DsspServiceCreationPort creationPort)
            : base(creationPort)
        {
        }

        protected override void Start()
        {
            Activate(Arbiter.Interleave(
                new TeardownReceiverGroup(),
                new ExclusiveReceiverGroup
                (
                    Arbiter.ReceiveWithIterator<Connect>(true, _mainPort, ConnectHandler)
                ),
                new ConcurrentReceiverGroup
                (
                    Arbiter.Receive<DsspDefaultLookup>(true, _mainPort, DefaultLookupHandler),
                    Arbiter.ReceiveWithIterator<bumper.Replace>(true, _notifyPort, OnBumperReplaceHandler),
                    Arbiter.ReceiveWithIterator<bumper.Update>(true, _notifyPort, OnBumperUpdateHandler),
                    Arbiter.ReceiveWithIterator<ContactSensorReplace>(true, _mainPort, ReplaceHandler),
                    Arbiter.ReceiveWithIterator<ContactSensorUpdate>(true, _mainPort, UpdateHandler),
                    Arbiter.ReceiveWithIterator<Subscribe>(true, _mainPort, SubscribeHandler)
                )
            ));

            DirectoryInsert();
        }

        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public IEnumerator<ITask> GetHandler(Get get)
        {
            get.ResponsePort.Post(_state);
            yield break;
        }

        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public IEnumerator<ITask> SubscribeHandler(Subscribe subscribe)
        {
            SubscribeHelper(_subMgrPort, subscribe.Body, subscribe.ResponsePort);
            yield break;
        }


        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ITask> UpdateHandler(ContactSensorUpdate update)
        {
            SendNotification<ContactSensorUpdate>(_subMgrPort, update.Body);
            yield break;
        }


        [ServiceHandler(ServiceHandlerBehavior.Concurrent)]
        public virtual IEnumerator<ITask> ReplaceHandler(ContactSensorReplace replace)
        {
            SendNotification<ContactSensorReplace>(_subMgrPort, replace.Body);
            yield break;
        }

        bumper.ContactSensorArrayOperations _bumperPort;
        bumper.ContactSensorArrayOperations _notifyPort = new bumper.ContactSensorArrayOperations();
        Port<Shutdown> _bumperShutdown = null;

        [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
        public virtual IEnumerator<ITask> ConnectHandler(Connect update)
        {
            UriBuilder builder = new UriBuilder(Schemes.DsspTcp, "localhost", 0, ServicePaths.InstanceDirectory);
            builder.Scheme = new Uri(ServiceInfo.Service).Scheme;

            ds.DirectoryPort port = ServiceForwarder<ds.DirectoryPort>(builder.Uri);
            ds.Get get = new ds.Get();

            port.Post(get);
            ServiceInfoType[] list = null;

            yield return Arbiter.Choice(get.ResponsePort,
                delegate(ds.GetResponseType response)
                {
                    list = response.RecordList;
                },
                delegate(Fault fault)
                {
                    list = new ServiceInfoType[0];
                    LogError(fault);
                }
            );

            foreach (ServiceInfoType info in list)
            {
                if (info.Contract == bumper.Contract.Identifier)
                {
                    LogInfo("### Bumper service founded");

                    _bumperPort = ServiceForwarder<bumper.ContactSensorArrayOperations>(info.Service);
                    _bumperShutdown = new Port<Shutdown>();

                    bumper.ReliableSubscribe subscribe = new bumper.ReliableSubscribe(
                        new ReliableSubscribeRequestType(5)
                    );
                    subscribe.NotificationPort = _notifyPort;
                    subscribe.NotificationShutdownPort = _bumperShutdown;

                    _bumperPort.Post(subscribe);

                    yield return Arbiter.Choice(
                        subscribe.ResponsePort,
                        delegate(SubscribeResponseType response)
                        {
                            LogInfo("### Subscribed to " + info.Service.ToString());
                        },
                        delegate(Fault fault)
                        {
                            _bumperShutdown = null;
                            LogError(fault);
                        }
                    );
                }
            }

            update.ResponsePort.Post(DefaultUpdateResponseType.Instance);
            yield break;
        }

        IEnumerator<ITask> OnBumperUpdateHandler(bumper.Update update)
        {
            _state = update.Body;

            _mainPort.Post(new ContactSensorUpdate(update.Body));

            yield break;
        }

        IEnumerator<ITask> OnBumperReplaceHandler(bumper.Replace replace)
        {            
            _mainPort.Post(new ContactSensorReplace(replace.Body));

            yield break;
        }

    }

}
